package org.hibernate.mapping;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import javax.persistence.Embeddable;

import org.hibernate.boot.Metadata;

import com.kinyx.framework.enums.impl.ErrorCodeX;
import com.kinyx.framework.exception.KRuntimeException;

public class Utils {

	public static PersistentClass getEntityBinding(final Table table, final Metadata metadata) {
		final Collection<PersistentClass> entityBindings = metadata.getEntityBindings();
		for (final PersistentClass entityBinding : entityBindings) {
			if (entityBinding.getTable().equals(table)) {
				return entityBinding;
			}
		}
		throw new KRuntimeException(ErrorCodeX.NeverHappens);
	}

	public static Class<?> getEntityClass(final Table table, final Metadata metadata) {
		return getEntityBinding(table, metadata).getMappedClass();
	}

	public static List<Column> getColumnBinding(final Table table, final Value value) {
		if (value instanceof Component) {
			final Component componentValue = Component.class.cast(value);
			final List<Column> columns = new ArrayList<>(componentValue.getPropertySpan());
			@SuppressWarnings("unchecked")
			final Iterator<Property> propertyIterator = componentValue.getPropertyIterator();
			while (propertyIterator.hasNext()) {
				final Property property = propertyIterator.next();
				columns.addAll(getColumnBinding(table, property.getValue()));
			}
			return columns;
		} else if (value instanceof SimpleValue) {
			// getColumnIterator方法返回值是包含主键字段的。
			final Iterator<Column> columnIterator = table.getColumnIterator();
			while (columnIterator.hasNext()) {
				final Column column = columnIterator.next();
				if (value.equals(column.getValue())) {
					return Collections.singletonList(column);
				}
			}
			throw new KRuntimeException(ErrorCodeX.NeverHappens);
		}
		throw new KRuntimeException(ErrorCodeX.NotImplemented);
	}

	public static Field getField(final Class<?> entityClass, final Property property) {
		try {
			return entityClass.getDeclaredField(property.getName());
		} catch (NoSuchFieldException | SecurityException e) {
			throw new KRuntimeException(ErrorCodeX.NeverHappens);
		}
	}

	public static Field getField(final Class<?> embeddableClass, final Column column) {
		try {
			return embeddableClass.getDeclaredField(column.getName());
		} catch (NoSuchFieldException | SecurityException e) {
			throw new KRuntimeException(ErrorCodeX.NeverHappens);
		}
	}

	public static List<ColumnWrapper> toColumnWrapper(final Table table, final Class<?> entityClass, final Property property) {
		final List<Column> columns = getColumnBinding(table, property.getValue());
		if (columns.size() == 1) { return Collections.singletonList(new ColumnWrapper(table, entityClass, columns.get(0), getField(entityClass, property))); }
		final List<ColumnWrapper> list = new ArrayList<>(columns.size());
		columns.forEach(column -> {
			final Field field = getField(entityClass, property);
			final Class<?> fieldType = field.getType();
			if (fieldType.getAnnotation(Embeddable.class) == null) {
				throw new KRuntimeException(ErrorCodeX.NotImplemented);
			}
			list.add(new ColumnWrapper(table, entityClass, column, getField(fieldType, column)));
		});
		return list;
	}

}
